需要
RK3588-uboot源码支持,需要重新编写完善:
- uboot部分
- EFI加载过程解读
- 加载内核时会关闭MMU,中断等操作的清晰讲解
- uboot下的image.h,efi.h,efi_iamge_loade.c
这里会简单分析
uboot启动末期,以及内核启动初期的代码。
Image
具体的构建指令
在我们进行分析前我们需要对Image的组成进行分析,该文件的生成是在:
// arch/arm64/boot/Makefile
targets := Image Image.bz2 Image.gz Image.lz4 Image.lzma Image.lzo Image.zst
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
// scripts/Makefile.lib
# Objcopy
# ---------------------------------------------------------------------------
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
// Makefile
OBJCOPY = $(CROSS_COMPILE)objcopy展开后是这样的:
aarch64-none-linux-gnu-objcopy $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) vmlinux Image可以看到实际上是使用objcopy复制vmlinux到Image。
vmlinux与Image的区别
vmlinux是一个 未压缩的内核映像,通常是通过编译内核源代码生成的。这是一个 可执行文件,包含了内核的所有代码、数据和符号,它通常是ELF格式(Executable and Linkable Format)。vmlinux 具有完整的、包含调试符号和其他调试信息的内核映像文件,适用于调试和开发阶段。 它的特点是:
- 格式:通常是 ELF 格式。
- 内容:包含内核代码、数据、符号表、调试信息等。
- 生成:通过
make编译内核时生成。具体来说,执行make vmlinux或make会生成vmlinux文件。 - 用途:
- 主要用于 调试,因为它包含了符号表和调试信息。
- 在某些情况下也可以用作生成
Image的基础。
Image 是 Linux 内核的 压缩映像文件,通常用于引导。它是一个 压缩过的内核映像,大小比 vmlinux 小,通常用于直接加载到系统中启动内核。Image 也可能是 zImage 或 bzImage 的一个具体实例,取决于配置和架构。 它的特点是:
- 格式:通常是压缩格式(如 gzip 或 bzip2),因此它的文件名可能是
zImage、bzImage或Image.gz等。 - 内容:包含了内核代码、压缩后的数据、以及一些引导程序所需的启动信息。
- 生成:通过
make生成,通常在执行make Image或make bzImage时生成。zImage:通常用于小型设备或嵌入式设备,它是一个较小的压缩内核映像。bzImage:通常用于支持更大内存的设备,包含更复杂的引导代码。 通过上面可以知道,Image实际上是一个RAW的可执行文件,仅仅包含少量信息和内核可执行机器码。 那么为什么需要分析它,因为在uboot跳转到内核时,不单单时简单的跳转到Image的第一个指令开始执行,而是会简单的解析Image的头部信息,根据此进行跳转。
Image的结构
在Image文件的头部,是一串PE/COFF的头,他是Windows上exe常用的格式,也是EFI的一种实现。 

UBOOT的启动
启动流程
解析Image
内核的启动
head.S
该文件位于arch/arm64/kernel中,内核的第一条指令在其中。同时它在编译后,会产生PE/COFF头。我们来看看该部分代码:
/*
* Kernel startup entry point.
* ---------------------------
*
* The requirements are:
* MMU = off, D-cache = off, I-cache = on or off,
* x0 = physical address to the FDT blob.
*
* Note that the callee-saved registers are used for storing variables
* that are useful before the MMU is enabled. The allocations are described
* in the entry routines.
*/
__HEAD
/*
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
*/
efi_signature_nop // special NOP to identity as PE/COFF executable
b primary_entry // branch to kernel start, magic
.quad 0 // Image load offset from start of RAM, little-endian
le64sym _kernel_size_le // Effective size of kernel image, little-endian
le64sym _kernel_flags_le // Informative flags, little-endian
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.ascii ARM64_IMAGE_MAGIC // Magic number
.long .Lpe_header_offset // Offset to the PE header.
__EFI_PE_HEADER
__INIT这里需要结合arch/arm64/kernel/vmlinux.lds来分析:
OUTPUT_ARCH(aarch64)
ENTRY(_text)
jiffies = jiffies_64;
PECOFF_FILE_ALIGNMENT = 0x200;
SECTIONS
{
/DISCARD/ : { *(.exitcall.exit) *(.discard) *(.discard.*) *(.modinfo) *(.gnu.version*) }
/DISCARD/ : {
*(.interp .dynamic)
*(.dynsym .dynstr .hash .gnu.hash)
}
. = ((((-(((1)) << ((((39))) - 1)))) + (0x08000000)));
.head.text : {
_text = .;
KEEP(*(.head.text))
}接着我们在include/linux/init.h中找到对__HEAD的宏定义:
#define __HEAD .section ".head.text","ax"结合链接脚本来看,也就是说__HEAD接下来的代码会放在最开始的位置。这里的.表示当前位置,而_text和KEEP(*(.head.text))都意味着section:.head.text的数据会从首部线性排列。 我们可以验证,在Image中的首部是PE的标志(MZ),接着是code1,text_offset,image_size。使用HEX查看:
上面解析的信息是(注释是对应head.S中的指令):
4D 5A 40 FA = "MZ" // 对应efi_signature_nop(ccmp x18, #0, #0xd, pl)
19 b4 4f 14 = 指令码 // b primary_entry
00 00 00 00 00 00 00 00 = 内核加载偏移量 // .quad 0
00 00 de 01 00 00 00 00 = 内核大小 // le64sym _kernel_size_le
......上面的ccmp x18, #0, #0xd, pl指令通过比较巧妙的操作,使得编译生成MZ的头。所以PE/COFF头实际上是由汇编代码配合链接脚本生成的。 在解析完成EFI的头部后,就会执行code0,code1,我们根据文档内核(ARM64)booting可以知道:
所以在b primary_entry后,我们可以按正常的代码调用逻辑得到以下的调用栈:
primary_entry
__primary_switch
__primary_switched
start_kernel这里就跳入了start_kernel,它是位于init/main.c中的一个函数,含有启动内核各部分的主流程函数。 到这里意味着,uboot已经切换到内核,并且进入主初始化程序。
参考
https://krinkinmu.github.io/2023/08/21/how-u-boot-loads-linux-kernel.htmlhttps://www.kernel.org/doc/Documentation/arm64/booting.txthttps://www.cnblogs.com/Iflyinsky/p/18149167https://zhuanlan.zhihu.com/p/343176381https://docs.kernel.org/admin-guide/efi-stub.htmlhttps://blog.csdn.net/jasonactions/article/details/111223097https://community.nxp.com/pwmxy87654/attachments/pwmxy87654/Layerscape%40tkb/153/1/ARM64 Kernel Booting Process.pdf